package com.working.libview.widget

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.animation.ValueAnimator.AnimatorUpdateListener
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.SparseArray
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.core.util.forEach
import com.working.libcommon.util.PointFEvaluator
import com.working.libview.R
import sp


class SplashView(
    context: Context,
    attrs: AttributeSet?,
    defStyleAttr: Int,
    defStyleRes: Int
) : View(context, attrs, defStyleAttr, defStyleRes) {

    constructor(context: Context) : this(context, null)
    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : this(
        context,
        attrs,
        defStyleAttr,
        0
    )

    private val DEFAULT_LOGO = "wanAndroid"
    private val ANIM_LOGO_DURATION = 1500
    private val ANIM_LOGO_GRADIENT_DURATION = 1500
    private val ANIM_LOGO_TEXT_SIZE = 30.sp
    private val ANIM_LOGO_TEXT_COLOR = Color.BLACK
    private val ANIM_LOGO_GRADIENT_COLOR = Color.RED

    private val mSplashCharacters = SparseArray<String>()
    private val mQuietPoints = SparseArray<PointF>()
    private val mRadonPoints = SparseArray<PointF>()
    private val mOffsetAnimator: ValueAnimator by lazy {
        ValueAnimator.ofFloat(0f, 1f).apply {
            interpolator =
                AccelerateDecelerateInterpolator()
            addUpdateListener(AnimatorUpdateListener { animation ->
                if (mQuietPoints.size() <= 0 || mRadonPoints.size() <= 0) {
                    return@AnimatorUpdateListener
                }
                mOffsetAnimProgress = animation.animatedValue as Float
                invalidate()
            })
            addListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    if (isShowGradient) {
                        isOffsetAnimEnd = true
                        mPaint.shader = mLinearGradient
                        mGradientAnimator.start()
                    }
                }
            })
        }
    }
    private val mGradientAnimator: ValueAnimator by lazy {
        ValueAnimator.ofInt(0, 2 * mWidth).apply {
            addUpdateListener { animation ->
                mMatrixTranslate = animation.animatedValue as Int
                invalidate()
            }
        }
    }
    private val mPaint: Paint by lazy {
        Paint().apply {
            isAntiAlias = true
            style = Paint.Style.FILL
            strokeCap = Paint.Cap.ROUND
        }
    }
    private var mTextPadding = 0
    private var mTextColor = 0
    private var mTextSize = 0
    private var mOffsetAnimProgress = 0f
    private var mOffsetDuration = 0
    private var isOffsetAnimEnd = false
    private var mGradientDuration = 0
    private val mLinearGradient by lazy {
        LinearGradient(
            -mWidth.toFloat(),
            0f,
            0f,
            0f,
//            intArrayOf(mTextColor, mGradientColor, mTextColor),
            intArrayOf(mTextColor, Color.RED, Color.YELLOW, Color.GREEN, Color.BLUE, mTextColor),
            floatArrayOf(0f, 0.2f, 0.4f, 0.6f, 0.8f, 1f),
            Shader.TileMode.CLAMP
        )
    }
    private var mGradientColor = 0
    private val mGradientMatrix by lazy { Matrix() }
    private var mMatrixTranslate = 0
    private var isAutoPlay = false
    private var mWidth = 0
    private var mHeight = 0
    private var isShowGradient = false
    private var mLogoOffset = 0
    private var mGradientListener: Animator.AnimatorListener? = null

    init {
        val ta = context.obtainStyledAttributes(attrs, R.styleable.SplashView)
        var logoName = ta.getString(R.styleable.SplashView_splashText)
        isAutoPlay = ta.getBoolean(R.styleable.SplashView_splashAutoPlay, true)
        isShowGradient = ta.getBoolean(R.styleable.SplashView_splashShowGradient, true)
        mOffsetDuration =
            ta.getInt(R.styleable.SplashView_splashOffsetAnimDuration, ANIM_LOGO_DURATION)
        mGradientDuration =
            ta.getInt(R.styleable.SplashView_splashAnimDuration, ANIM_LOGO_GRADIENT_DURATION)
        mTextColor = ta.getColor(R.styleable.SplashView_splashTextColor, ANIM_LOGO_TEXT_COLOR)
        mGradientColor =
            ta.getColor(R.styleable.SplashView_splashGradientColor, ANIM_LOGO_GRADIENT_COLOR)
        mTextSize =
            ta.getDimensionPixelSize(R.styleable.SplashView_splashTextSize, ANIM_LOGO_TEXT_SIZE)
        ta.recycle()
        if (logoName.isNullOrBlank()) {
            logoName = DEFAULT_LOGO // default logo
        }
        fillSplashCharacters(logoName)
        initPaint()
        initOffsetAnimation()
    }

    // fill the text to array
    private fun fillSplashCharacters(logoName: String) {
        if (logoName.isBlank()) {
            return
        }
        if (mSplashCharacters.size() > 0) {
            mSplashCharacters.clear()
        }
        for (i in logoName.indices) {
            val c = logoName[i]
            val s = c.toString()
            mSplashCharacters.put(i, s)
        }
    }

    private fun initPaint() {
        mPaint.textSize = mTextSize.toFloat()
        mPaint.color = mTextColor
    }

    // init the translation animation
    private fun initOffsetAnimation() {
        mOffsetAnimator.duration = mOffsetDuration.toLong()
    }

    // init the gradient animation
    private fun initGradientAnimation() {
        if (mWidth == 0) {
            return
        }
        if (mGradientListener != null) {
            mGradientAnimator.addListener(mGradientListener)
        }
        mGradientAnimator.duration = mGradientDuration.toLong()
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        if (visibility == VISIBLE && isAutoPlay) {
            mOffsetAnimator.start()
        }
    }

    override fun onDetachedFromWindow() {
        // release animation
        if (mOffsetAnimator.isRunning) {
            mOffsetAnimator.cancel()
        }
        if (mGradientAnimator.isRunning) {
            mGradientAnimator.cancel()
        }
        super.onDetachedFromWindow()
    }

    /**
     * 监听offset动画状态
     *
     * @param listener AnimatorListener
     */
    fun addOffsetAnimListener(listener: Animator.AnimatorListener?) {
        mOffsetAnimator.addListener(listener)
    }

    /**
     * 监听gradient动画状态
     *
     * @param listener AnimatorListener
     */
    fun addGradientAnimListener(listener: Animator.AnimatorListener?) {
        mGradientListener = listener
    }

    /**
     * 开启动画
     */
    fun startAnimation() {
        if (visibility == VISIBLE) {
            if (mOffsetAnimator.isRunning) {
                mOffsetAnimator.cancel()
            }
            isOffsetAnimEnd = false
            mOffsetAnimator.start()
        }
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        mWidth = w
        mHeight = h
        initLogoCoordinate()
        initGradientAnimation()
    }

    private fun initLogoCoordinate() {
        if (mWidth == 0 || mHeight == 0) {
            return
        }
        val centerY: Float = mHeight / 2f + mPaint.textSize / 2 + mLogoOffset
        // calculate the final xy of the text
        var totalLength = 0f
        for (i in 0 until mSplashCharacters.size()) {
            val str = mSplashCharacters[i]
            val currentLength = mPaint.measureText(str)
            totalLength += if (i != mSplashCharacters.size() - 1) {
                currentLength + mTextPadding
            } else {
                currentLength
            }
        }
        // the draw width of the logo must small than the width of this AnimLogoView
        check(totalLength <= mWidth) { "The text of logoName is too large that this view can not display all text" }
        var startX = (mWidth - totalLength) / 2
        if (mQuietPoints.size() > 0) {
            mQuietPoints.clear()
        }
        for (i in 0 until mSplashCharacters.size()) {
            val str = mSplashCharacters[i]
            val currentLength = mPaint.measureText(str)
            mQuietPoints.put(i, PointF(startX, centerY))
            startX += currentLength + mTextPadding
        }
        // generate random start xy of the text
        if (mRadonPoints.size() > 0) {
            mRadonPoints.clear()
        }
        for (i in 0 until mSplashCharacters.size()) {
            mRadonPoints.put(
                i,
                PointF(
                    Math.random().toFloat() * mWidth,
                    Math.random().toFloat() * mHeight
                )
            )
        }
    }

    override fun onDraw(canvas: Canvas) {
        if (!isOffsetAnimEnd) { // offset animation
            mPaint.alpha = 255f.coerceAtMost(255 * mOffsetAnimProgress + 100).toInt()
            mSplashCharacters.forEach { key, value ->
                val currentP = PointFEvaluator.evaluate(
                    mOffsetAnimProgress,
                    mRadonPoints[key],
                    mQuietPoints[key]
                )
                canvas.drawText(value, currentP.x, currentP.y, mPaint)
            }
        } else { // gradient animation
            for (i in 0 until mQuietPoints.size()) {
                val quietP = mQuietPoints[i]
                canvas.drawText(mSplashCharacters[i], quietP.x, quietP.y, mPaint)
            }
            mGradientMatrix.setTranslate(mMatrixTranslate.toFloat(), 0F)
            mLinearGradient.setLocalMatrix(mGradientMatrix)
        }
    }

    /**
     * 设置logo文字动效时长
     *
     * @param duration 动效时长
     */
    fun setOffsetAnimDuration(duration: Int) {
        mOffsetDuration = duration
        initOffsetAnimation()
    }

    /**
     * 设置logo文字渐变颜色
     *
     * @param gradientColor 渐变颜色
     */
    fun setGradientColor(gradientColor: Int) {
        mGradientColor = gradientColor
    }

    /**
     * 设置是否显示logo文字渐变
     *
     * @param isShowGradient 是否显示logo渐变动效
     */
    fun setShowGradient(isShowGradient: Boolean) {
        this.isShowGradient = isShowGradient
    }


}